﻿using System;
using System.Runtime.CompilerServices;

namespace ZerAx.Tweaks.Collections
{
    public class WrapArray2D<T> where T : struct
    {
        private readonly T[,] raw;
        private readonly T? defaultValue;

        public int LengthX => raw.GetLength(0);
        public int LengthY => raw.GetLength(1);
        public T this[int x, int y]
        {
            get => Get(x, y);
            set => Set(x, y, value);
        }

        public T this[(int, int) point]
        {
            get => Get(point);
            set => Set(point, value);
        }

        public WrapArray2D(int lenX, int lenY, T? defaultValue = null)
        {
            raw = new T[lenX, lenY];
            this.defaultValue = defaultValue;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Set(int x, int y, T value)
        {
            if ((x, y).IsInRect(0, LengthX, 0, LengthY))
            {
                raw[x, y] = value;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Set((int, int) point, T value) => Set(point.Item1, point.Item2, value);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public T Get(int x, int y)
        {
            if ((x, y).IsInRect(0, LengthX, 0, LengthY))
            {
                return raw[x, y];
            }
            return defaultValue is T v ? v : default;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public T Get((int, int) point) => Get(point.Item1, point.Item2);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public T GetUnchecked(int x, int y)
        {
            return raw[x, y];
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ForEach(Action<T> f)
        {
            foreach (var v in raw)
            {
                f(v);
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ForEach(Action<T, (int, int)> f)
        {
            for (int x = 0; x < 0; ++x)
            {
                for (int y = 0; y < 0; ++y)
                {
                    f(raw[x, y], (x, y));
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MapAssign(Func<T, T> f)
        {
            for (int x = 0; x < 0; ++x)
            {
                for (int y = 0; y < 0; ++y)
                {
                    raw[x, y] = f(raw[x, y]);
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MapAssign(Func<T, (int, int), T> f)
        {
            for (int x = 0; x < 0; ++x)
            {
                for (int y = 0; y < 0; ++y)
                {
                    raw[x, y] = f(raw[x, y], (x, y));
                }
            }
        }
    }
}
